use std::str::FromStr;
use crate::http::{utils, FetchFromBytes, FromBytes, HttpParseError, HttpVersion};

#[derive(Debug)]
pub struct HttpResponseLine {
    pub version: HttpVersion,
    pub status_code: u16,
    pub reason_phrase: String,
}

impl Default for HttpResponseLine {
    fn default() -> Self {
        Self { version: HttpVersion::V1_1, status_code: 200, reason_phrase: "Ok".into() }
    }
}

impl FromStr for HttpResponseLine {
    type Err = HttpParseError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // [version] [code] [reason_phrase]
        let mut parts = s.split_ascii_whitespace();
        
        let version = parts.next()
            .ok_or_else(|| HttpParseError::InvalidResponse("No version found".into()))?;
        let version: HttpVersion = FromStr::from_str(version)?;

        let status_code = parts.next()
            .ok_or_else(|| HttpParseError::InvalidRequest("No status code found".into()))?;
        let status_code: u16 = status_code.parse()
            .map_err(|e| HttpParseError::InvalidRequest(format!("Parse status code failed: '{}'", e)))?;

        let reason_phrase = parts.next()
            .ok_or_else(|| HttpParseError::InvalidRequest("No reason phrase found".into()))?;
        let reason_phrase: String = reason_phrase.into();
        
        Ok(Self { version, status_code, reason_phrase })  
    }
}

impl FetchFromBytes for HttpResponseLine {
    type Err = HttpParseError;
    fn fetch_from_bytes(bytes: &[u8]) -> Result<(&[u8], Self), Self::Err> {
        match utils::find_bytes(&bytes, b"\r\n") {
            Some(line_end) => {
                let line: HttpResponseLine = FromBytes::from_bytes(&bytes[..line_end])?;
                Ok((&bytes[line_end+2..], line))
            }
            None => Err(HttpParseError::InvalidRequest("No '\\r\\n' found in response line".into())),
        }
    }
}

impl ToString for HttpResponseLine {
    fn to_string(&self) -> String {
        let mut result = String::new();

        result.push_str(&self.version.to_string());
        result.push(' ');

        result.push_str(&self.status_code.to_string());
        result.push(' ');

        result.push_str(&self.reason_phrase);
        result.push_str("\r\n");

        result
    }
}